#include <vector>
#include <algorithm>

#include "namelister.h"

//setup NameLister object.
NameLister::NameLister():
    itsIdCounter(0)
{
    //ctor
}

//destructor. clean up dynamically allocated remains.
NameLister::~NameLister()
{
    //while itsNameList is not empty
    while(itsNameList.size())
    {
        //Delete the dynamically allocated Name objects
        Name* name = NULL;
        name = itsNameList.back();
        itsNameList.pop_back();
        delete name;
    }
}

//Uses input file to generate/append to itsNameList.
void NameLister::GenerateNameList(File& file)
{
    //setup local stringstream ptr for the input file's buffer.
    std::stringstream* FileBufferSS = NULL;
    FileBufferSS = file.GetPtrToFileBufferSS();

    //loop till broken out.
    while(true)
    {
        //setup name ptr and call Stage1() to scan and possible setup a new Name object.
        Name* name = NULL;
        name = Stage1(FileBufferSS);

        //if no name, break out and return.
        if(name == NULL)
            break;

        //if name, call stage2 and stage3 to continue seting up the Name object.
        Stage2(name, FileBufferSS);
        Stage3(name, FileBufferSS);

        //when finished setting up the Name object, add it to the NameList.
        itsNameList.push_back(name);
    }
}

//return itsNameList
NAMELIST NameLister::GetNameList()
{
    return itsNameList;
}

//Scans for incoming names and if found, sets up a Name object and stores the type.
//Asignes Type value
Name* NameLister::Stage1(std::stringstream* FileBufferSS)
{
    //Setup local Name pointer
    Name* name = NULL;

    //Setup types to scan for
    std::vector<std::string> typeVec;
    typeVec.push_back("int");
    typeVec.push_back("float");
    typeVec.push_back("short");
    typeVec.push_back("long");
    typeVec.push_back("void");
    typeVec.push_back("char");

    //while eof is not reached..
    while(!FileBufferSS->eof())
    {
        //Setup and
        std::string type;
        *FileBufferSS >> type;

        //find type
        std::vector<std::string>::iterator iter;
        iter = std::find(typeVec.begin(), typeVec.end(), type);

        //if type found
        if(iter != typeVec.end())
        {
            //create a new Name object and set local name ptr to its address
            name = new Name(*iter);
            //and break out of loop.
            break;
        }
    }

    //return address of the Name object
    return name;
}

//Assumes name is a Stage1() created name object and assignes a name/identifier to it.
//Also asignes Type2 value.
void NameLister::Stage2(Name* name, std::stringstream* FileBufferSS)
{
    //setup C++ string to hold the name/identifier of a variable/funtion.
    //and name into it.
    std::string nameStr;
    *FileBufferSS >> nameStr;

    //get and save file address of name/identifier
    UINT nameAddr = 0;
    nameAddr = FileBufferSS->tellg() - nameStr.length();
    name->SetPointer(nameAddr);

    //Quick hack to fix issue 1
    char lastChar = nameStr[nameStr.length() - 1];
    if(lastChar == ';')
    {
        FileBufferSS->unget();
        nameStr.erase(nameStr.end() - 1);
    }

    //setup isFunc flag. used to tell if name/identifier is a function.
    bool isFunc = false;

    //Scan for function identifier '('
    for(UINT i = 0; i <= nameStr.length(); ++i)
    {
        //if function identifier '(' found..
        if(nameStr[i] == '(')
        {
            //erase function identifier '(' from nameStr
            nameStr.erase(i, nameStr.length());

            //correct the get ptr offset for FileBufferSS
            //TODO Fix warning..
            FileBufferSS->seekg(FileBufferSS->tellg() - i);

            //set isFunc flag..
            isFunc = true;
        }
    }
    //if is function, set type 2 as T2FUN else set as T2VAR for a variable
    if(isFunc)
    {
        name->SetType2("function");
    }
    else
    {
        name->SetType2("variable");
    }

    //Set the Name objects name/identifier
    name->SetName(nameStr);

}

//Assumes name was processed by stage2() and assignes an id and a Type3 value.
void NameLister::Stage3(Name* name, std::stringstream* FileBufferSS)
{
    //set name id and increment id counter, all in one..
    name->SetId(itsIdCounter++);

    while(1)
    {
        //setup string to hold type3 in. (decl, impl, or assi)
        char type3Str;
        type3Str = FileBufferSS->get();

        //if declaration, set it as so..
        if(type3Str == ';')
        {
            name->SetType3("declaration");
            break;
        }

        //if implementation, set it as so..
        else if(type3Str == '{')
        {
            name->SetType3("implementation");
            break;
        }

        //if assignment, set it as so..
        else if(type3Str == '=')
        {
            name->SetType3("assignment");
            break;
        }
    }
}

